﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.Entities.Universes;
using Sustainalytics.Entities;
using Sustainalytics.Universes.Synchronization;
using Sustainalytics.Utils.Exceptions.Universes;
using Sustainalytics.Auth.Claims;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Moq;
using Sustainalytics.DataLayer;

namespace Sustainalytics.Universes.Tests
{
    [TestClass]
    public class UniversesSynchronizationTests
    {
        IUniverseSynchronization _universesSync = null;
        Mock<IUniversesRepository> _universeRepository = null;
        List<SlimCompany> _companies = null;
        List<Account> _accounts = null;
        List<FeatureUniverse> _featureUniverses = null;
        List<UniverseCompany> _universeCompanies = null;
        List<Universe> _universes = null;
        List<UploadErrorLog> _uploadErrorLogs = null;
        UploadLog _uploadLog = null;

        [TestInitialize]
        public void Initialize()
        {
            #region InitializeEntities
           
            _accounts = new List<Account>();
            _accounts.Add(new Entities.Account()
            {
                Id = new Guid("596d5f79-9748-e411-9e5f-d89d67630ddc"),
                Name = "Sustainalytics",
                AccountManager = new Entities.AccountManager()
                {
                    Email = "mihai.stanescu@sustainalytics.com",
                    FirstName = "Mihai",
                    LastName = "Stanescu",
                    FullName = "Mihai Stanescu",
                    Title = "SD"
                },
                LastCrmSync = DateTime.Now,
                ModifiedOn = DateTime.Today,
                Office = "Timisoara",
                RelationshipType = Entities.RelationshipType.Client,
                Status = Entities.Status.Active
            });
            _accounts.Add(new Entities.Account()
            {
                Id = new Guid("a4ae61fc-6012-e211-9a22-3c4a92dbc8a2"),
                Name = "BNP Paribas",
                AccountManager = new Entities.AccountManager()
                {
                    Email = "first.last@sustainalytics.com",
                    FirstName = "first",
                    LastName = "last",
                    FullName = "first last",
                    Title = "SD"
                },
                LastCrmSync = DateTime.Now,
                ModifiedOn = DateTime.Today,
                Office = "Amsterdam",
                RelationshipType = Entities.RelationshipType.Prospect,
                Status = Entities.Status.Active
            });
            _accounts.Add(new Entities.Account()
            {
                Id = new Guid("2A5B5189-2465-4A75-8D4B-BCC25E5E43FF"),
                Name = "FakeAccount",
                AccountManager = new Entities.AccountManager()
                {
                    Email = "first2.last2@sustainalytics.com",
                    FirstName = "first2",
                    LastName = "last2",
                    FullName = "first2 last2",
                    Title = "SD"
                },
                LastCrmSync = DateTime.Now,
                ModifiedOn = DateTime.Today,
                Office = "Amsterdam",
                RelationshipType = Entities.RelationshipType.Prospect,
                Status = Entities.Status.Active
            });


            _companies = new List<SlimCompany>();
            PopulateCompanies();

            _featureUniverses = new List<FeatureUniverse>();
            _featureUniverses.Add(new FeatureUniverse()
            {
                AccountId = Guid.NewGuid(),
                Feature = AccountFeature.GlobalCompactWatchlistAndNonCompliant,
                UniverseIds = new List<int>() { 2, 5, 71, 89 }
            });
            _featureUniverses.Add(new FeatureUniverse()
            {
                AccountId = Guid.NewGuid(),
                Feature = AccountFeature.ScreeningToolESGEvents,
                UniverseIds = new List<int>() { 42, 6, 8, 12, 63 }
            });

            _universeCompanies = new List<UniverseCompany>();
            _universeCompanies.Add(new UniverseCompany() { CIQID = _companies.First().CIQID, CompanyId = _companies.First().CompanyId, UniverseId = 7, LastUpdated = DateTime.UtcNow });
            _universeCompanies.Add(new UniverseCompany() { CIQID = _companies.Last().CIQID, CompanyId = _companies.Last().CompanyId, UniverseId = 38, LastUpdated = DateTime.UtcNow });

            _universes = new List<Universe>();
            _universes.Add(new Universe() { AccountId = _accounts.First().Id, Name = "ProductInvolvement", Id = 4, Created = DateTime.UtcNow, LastUpdated = DateTime.UtcNow });
            _universes.Add(new Universe() { AccountId = _accounts.Last().Id, Name = "GlobalCompact", Id = 6, Created = DateTime.UtcNow, LastUpdated = DateTime.UtcNow });

            _uploadLog = new UploadLog() { Id= Guid.NewGuid(), FileId = Guid.NewGuid(), Created = DateTime.UtcNow, Processed = false, UploadType = UploadType.UniversesUpload, UserId = Guid.NewGuid() };

            _uploadErrorLogs = new List<UploadErrorLog>();
            #endregion

            #region UniversesStubs

            _universeRepository = new Mock<IUniversesRepository>();

            _universeRepository.Setup(x=>x.AddFeatureUniverse(It.IsAny<FeatureUniverse>())).Callback((FeatureUniverse featureUniverse) =>
                {
                    if (_featureUniverses.Any(fu => fu.AccountId == featureUniverse.AccountId && fu.Feature == featureUniverse.Feature))
                        throw new DuplicateFeatureUniverseException();

                    _featureUniverses.Add(featureUniverse);
                });
            _universeRepository.Setup(m=>m.InsertBatchUniverseCompany(It.IsAny<List<UniverseCompany>>())).Callback((List<UniverseCompany> univList) =>
               
                {
                    _universeCompanies.AddRange(univList);
                })
            ;
            _universeRepository.Setup(m => m.AddUniverse(It.IsAny<Universe>())).Callback(
                (Universe universe) =>
                {
                    if (_universes.Any(u => u.Id == universe.Id))
                        throw new DuplicateUniverseException();

                    _universes.Add(universe);
                });

            _universeRepository.Setup(m=>m
                .AddUploadErrorBatchLog(It.IsAny<List<UploadErrorLog>>())).Callback((List<UploadErrorLog> listErrors) =>
                {
                    _uploadErrorLogs.AddRange(listErrors);
                });

            _universeRepository.Setup(m=>m
                .DeleteAllUniverseCompanies()).Callback(() =>
                {
                    _universeCompanies.Clear();
                });
            _universeRepository.Setup(m => m
                .DeleteAllUniverses ()).Callback(() =>
                {
                    _universes.Clear();
                });
            _universeRepository.Setup(m => m
                .DeleteUniverse(It.IsAny<int>())).Callback( (int universeId) =>
                {
                    _universes.RemoveAll(u => u.Id == universeId);
                });
            _universeRepository.Setup(m => m
                .GetFeatureUniverse(It.IsAny<Guid>(),It.IsAny<AccountFeature>())).Returns( (Guid accountId, AccountFeature feature) =>
                {
                    return _featureUniverses.SingleOrDefault(fu => fu.AccountId == accountId && fu.Feature == feature);
                });
            _universeRepository.Setup(m => m
                .GetFile(It.IsAny<Guid>())).Returns( (Guid fileId) =>
                {
                    var buffer = File.ReadAllBytes(@"./Resources/universes_upload.xlsx");
                    return buffer;
                });
            _universeRepository.Setup(m => m
                .GetUniverseCompany(It.IsAny<Guid>(),It.IsAny<int>())).Returns( (Guid companyId, int universeId) =>
                {
                    return
                        _universeCompanies.SingleOrDefault(
                            uc => uc.CompanyId == companyId && uc.UniverseId == universeId);
                });
            _universeRepository.Setup(m => m
                .GetUniverse(It.IsAny<string>(),It.IsAny<Guid>())).Returns( (string universeName, Guid accountId) =>
                {
                    return _universes.SingleOrDefault(u => u.Name == universeName && u.AccountId == accountId);
                });
            _universeRepository.Setup(m => m
                .GetUploadLog()).Returns(() => _uploadLog);

            _universeRepository.Setup(m => m
                .UpdateFeatureUniverses (It.IsAny<Guid>(),It.IsAny<AccountFeature>(),It.IsAny<List<int>>())).Callback( (Guid accountId, AccountFeature feature, List<int> universeIds) =>
                {
                    var featureUniv =
                        _featureUniverses.SingleOrDefault(fu => fu.AccountId == accountId && fu.Feature == feature);
                    if (featureUniv == null)
                        throw new ArgumentNullException();

                    featureUniv.UniverseIds = universeIds;
                });
            _universeRepository.Setup(m => m
                .AddUploadErrorLog(It.IsAny<UploadErrorLog>())).Callback( (UploadErrorLog uploadErrorLog) =>
                {
                    _uploadErrorLogs.Add(uploadErrorLog);
                });
            _universeRepository.Setup(m => m
                .GetUploadErrorLogs()).Returns(() => _uploadErrorLogs);


            _universeRepository.Setup(m => m
                .GetUniversesByAccountId(It.IsAny<Guid>())).Returns((Guid accountId) =>
                {
                    return _universes.Where(un => un.AccountId == accountId).ToList();
                });
            _universeRepository.Setup(m => m
                .DeleteAllFeatureUniverses()).Callback( () =>
                {
                    _featureUniverses.Clear();
                });

            _universeRepository.Setup(m => m
                .GetNextUniverseId()).Returns( () =>
                {
                    if (_universes.Any())
                        return _universes.Max(c => c.Id) + 1;
                    else
                        return 1;
                }
                );
            #endregion

            #region AccountsStub

            var accountsRepository = new Mock<IAccountsRepository>();
                accountsRepository.Setup(
                    m=>m.TryGetAccountById(It.IsAny<Guid>())).Returns( (Guid id) =>_accounts.SingleOrDefault(a => a.Id == id));
          
            #endregion
            #region ClientDwStub

            var _clientDwRepository = new Mock<IClientDwRepository>();
            _clientDwRepository.Setup(
                m => m.GetCompanyIdByCiqid(It.IsAny<string>())).Returns((string ciqid) =>
                {
                    var company = _companies.SingleOrDefault(c => c.CIQID == ciqid);
                    if (company == null)
                        throw new CompanyNotFoundException(
                            string.Format("The company with CIQID: '{0}' could not be found.", ciqid), ciqid);
                    return company.CompanyId;
                });

            _clientDwRepository.Setup(m => m.GetCompanyIdByCiqid(It.IsAny<List<string>>()))
                .Returns((List<string> ids) =>
                {
                    var ciqIds = ids.Select(c => c.ToUpperInvariant());
                    return _companies.Where(c => ciqIds.Contains(c.CIQID.ToUpperInvariant()))
                        .ToDictionary(c => c.CIQID, c => c.CompanyId);
                }
                );
            #endregion

            _universesSync = UniverseSynchronization.GetInstance(_universeRepository.Object, accountsRepository.Object, _clientDwRepository.Object);
        }

        public void PopulateCompanies()
        {
            _companies.Add(new SlimCompany() { CIQID = "IQ82857993", CompanyId = Guid.Parse("85890483-16FD-4C17-AC7C-00004BCACE85") });
            _companies.Add(new SlimCompany() { CIQID = "IQ32605", CompanyId = Guid.Parse("7BAFDBD0-2E9C-4048-8A85-000BBF41AF01") });
            _companies.Add(new SlimCompany() { CIQID = "IQ144617841", CompanyId = Guid.Parse("3FC1B393-D2D8-4884-ADA6-000D603CE6F5") });
            _companies.Add(new SlimCompany() { CIQID = "IQ5398366", CompanyId = Guid.Parse("74D03CA3-2342-44F7-8788-000DD163E763") });
            _companies.Add(new SlimCompany() { CIQID = "IQ3126223", CompanyId = Guid.Parse("E0F5BC12-EF7A-4274-A150-0015C4C8D872") });
            _companies.Add(new SlimCompany() { CIQID = "IQ48937213", CompanyId = Guid.Parse("D8F38328-5730-4DB2-A36C-002281DE5DCD") });
            _companies.Add(new SlimCompany() { CIQID = "IQ6481147", CompanyId = Guid.Parse("C629E3E6-619A-4C33-92B2-0023EE73FB95") });
            _companies.Add(new SlimCompany() { CIQID = "IQ3109204", CompanyId = Guid.Parse("D0A4CECD-B523-454B-961A-0025EC1B6D2A") });
            _companies.Add(new SlimCompany() { CIQID = "IQ11421485", CompanyId = Guid.Parse("8A2CDD1E-C1F0-4C00-965A-002D4FF4F81E") });
            _companies.Add(new SlimCompany() { CIQID = "IQ106775697", CompanyId = Guid.Parse("5B2775EA-3B5D-4543-A938-002F8F23FB82") });
            _companies.Add(new SlimCompany() { CIQID = "IQ1342644", CompanyId = Guid.Parse("A6832374-54A2-49D4-A65A-003438F8256A") });
            _companies.Add(new SlimCompany() { CIQID = "IQ883661", CompanyId = Guid.Parse("0D825CDD-64ED-4762-867A-0034ABEB98E7") });
            _companies.Add(new SlimCompany() { CIQID = "IQ97957", CompanyId = Guid.Parse("397DF76F-4498-4003-8B40-00387EF67E13") });
            _companies.Add(new SlimCompany() { CIQID = "IQ23697671", CompanyId = Guid.Parse("0CBE2A5E-19E3-41BE-A4B5-0038B6F61947") });
            _companies.Add(new SlimCompany() { CIQID = "IQ6459652", CompanyId = Guid.Parse("1477701F-EF80-4903-B893-0039D68326E9") });
            _companies.Add(new SlimCompany() { CIQID = "IQ143659899", CompanyId = Guid.Parse("D16553D7-DAAD-4349-8E14-004182C3353C") });
            _companies.Add(new SlimCompany() { CIQID = "IQ9681063", CompanyId = Guid.Parse("A3E8168A-0214-4953-AB54-0045A8DE69E8") });
            _companies.Add(new SlimCompany() { CIQID = "IQ325509", CompanyId = Guid.Parse("A8FD1A44-55BA-4818-A9C1-00544D36D9C2") });
            _companies.Add(new SlimCompany() { CIQID = "IQ5939268", CompanyId = Guid.Parse("9173A7B6-5B51-4AE6-BBDE-005C5B98A428") });
        }

        [TestCleanup]
        public void Cleanup()
        {

        }

        [TestMethod]
        public void SyncUniverses()
        {
            var syncContext = new Sustainalytics.Synchronization.Interface.SyncContext();
            syncContext.Report = new System.Collections.Concurrent.ConcurrentBag<string>();
            syncContext.GAConnectionStrings = new Dictionary<string, string>();
            syncContext.GAConnectionStrings.Add("ClientUniverses", string.Empty);
            _universesSync.SyncUniverses(syncContext);

            Assert.AreEqual(13, _universes.Count, "The number of universes is different than the expected one.");
            Assert.AreEqual(22, _universeCompanies.Count, "The number of universe companies is different than the expected one.");
            Assert.AreEqual(12, _featureUniverses.Count, "The number of feature universes is different than the expected one.");
            Assert.AreEqual(2, _uploadErrorLogs.Count, "The number of error logs is different than the expected one.");
            Assert.IsTrue(_universes.Any(u => u.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id), "There should be at least one universe corresponding to the filter.");
            Assert.IsTrue(_featureUniverses.Any(fu => fu.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id && fu.Feature == AccountFeature.CompanySearch), "There should be at least one feature universe corresponding to the filter.");
            Assert.IsTrue(_featureUniverses.Any(fu => fu.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id && fu.Feature == AccountFeature.ScreeningToolBusinessOverview), "There should be at least one feature universe corresponding to the filter.");
            Assert.IsTrue(_featureUniverses.Any(fu => fu.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id && fu.Feature == AccountFeature.ScreeningToolSocial), "There should be at least one feature universe corresponding to the filter.");
            Assert.IsTrue(_universeCompanies.Any(uc => uc.CIQID == "IQ82857993" && uc.UniverseId == _universes.Single(u => u.Name == "ESG Research" && u.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id).Id), "There should be at least one universe company corresponding to the filter.");
            Assert.IsTrue(_universeCompanies.Any(uc => uc.CIQID == "IQ82857993" && uc.UniverseId == _universes.Single(u => u.Name == "Events" && u.AccountId == _accounts.Single(acc => acc.Name == "Sustainalytics").Id).Id), "There should be at least one universe company corresponding to the filter.");
            Assert.IsFalse(_universeCompanies.Any(uc => uc.CIQID == "IQ894091" && uc.UniverseId == _universes.Single(u => u.Name == "ESG Research" && u.AccountId == _accounts.Single(acc => acc.Name == "FakeAccount").Id).Id), "There should not be any universe company corresponding to the filter.");
        }
    }

    public class SlimCompany
    {
        public Guid CompanyId { get; set; }
        public string CIQID { get; set; }
    }

    public class UploadedFile
    {
        public Guid FileId { get; set; }
        public byte[] ExcelFileArray { get; set; }
        public UploadedFile()
        {

        }
    }
}
